package com.truyen.service.impl;

import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.truyen.persistence.dao.AccountDao;
import com.truyen.persistence.entity.Account;
import com.truyen.service.UserService;
import com.truyen.utils.PagingInfo;
import com.truyen.utils.PropertyResolver;
import com.truyen.utils.UserUtils;

@Service("userDetailService")
public class UserDetailServiceImpl implements UserService, UserDetailsService {

	static Logger logger=Logger.getLogger(UserDetailServiceImpl.class);

	@Autowired
	private AccountDao accountDao;
	
	@Autowired
	private AuthenticationManager springAuthManager;
	
	@Autowired
	private PropertyResolver propertyResolver;
	
	@Autowired
	private Md5PasswordEncoder md5PasswordEncoder;
	
	@SuppressWarnings("deprecation")
	@Override
	public UserDetails loadUserByUsername(String email)
			throws UsernameNotFoundException, DataAccessException {
		String rootUsrName = propertyResolver.getValue(UserUtils.KEY_ROOTNAME);
		if (email.equals(rootUsrName)) {
			String rootPwd = propertyResolver.getValue(UserUtils.KEY_ROOTPWD);
			return UserUtils.createAdvanceAdmin(rootUsrName,
					md5PasswordEncoder.encodePassword(rootPwd, null));
		} else if(email.equals("openUser")) {
			logger.debug("Open user logged in");
			return UserUtils.createOpenUser(email, 
					md5PasswordEncoder.encodePassword("openpwd", null));
		} else {
			Account user = findItemByProperty("email",email);

			if (user != null) {
				if (user.getIsActive()) {
					return UserUtils.createActiveUser(user);
				}
			}

			throw new UsernameNotFoundException("Cannot find user", email);
		}
	}

	public List<Account> findByMobile(String mobile) {
		return accountDao.findByProperty("mobile", mobile);
	}

	public List<Account> findByLevel(int level) {
		return accountDao.findByProperty("level", level);
	}

	public List<Account> findByIsActive(boolean isActive) {
		return accountDao.findByProperty("isActive", isActive);
	}

	public List<Account> findByProperty(String property, Object value) {
		return accountDao.findByProperty(property, value);
	}

	public Account getCurrentLoggedInUser() {
		String rootUsrName = propertyResolver.getValue(UserUtils.KEY_ROOTNAME);
		Object principalObj = getSecurityContext().getAuthentication()
				.getPrincipal();
		if (principalObj != null
				&& principalObj instanceof UserDetails
				&& !((UserDetails) principalObj).getUsername().equals(
						rootUsrName)) {
			Account account = findItemByProperty("email",((UserDetails) principalObj).getUsername());
			account.setIsOnline(true);
			accountDao.merge(account);
			return account;
		} else if (principalObj instanceof UserDetails
				&& ((UserDetails) principalObj).getUsername().equals(
						rootUsrName)) {
			Account account = new Account();
			account.setNickName("Super admin");
			account.setEmail(rootUsrName);
			account.setPwd(md5PasswordEncoder.encodePassword(
					propertyResolver.getValue(UserUtils.KEY_ROOTPWD), null));
			account.setLevel(3);
			return account;
		}
		return null;
	}

	public SecurityContext getSecurityContext() {
		return SecurityContextHolder.getContext();
	}

	public boolean authenticate(String email, String password, HttpServletRequest request) {
		UsernamePasswordAuthenticationToken userAuth = new UsernamePasswordAuthenticationToken(
				email, password);
		userAuth.setDetails(new WebAuthenticationDetails(request));
		Authentication authentication = springAuthManager.authenticate(userAuth);
		if(authentication.isAuthenticated()) {
			getSecurityContext().setAuthentication(authentication);
			return true;
		}
		return false;
	}

	@Override
	public boolean authenticate(String email, String pwd) {
		UsernamePasswordAuthenticationToken userAuth = new UsernamePasswordAuthenticationToken(
				email, pwd);
		Authentication authentication = springAuthManager.authenticate(userAuth);
		if(authentication.isAuthenticated()) {
			getSecurityContext().setAuthentication(authentication);
			return true;
		}
		
		return false;
	}

	@Transactional(rollbackFor = Exception.class)
	public void save(Account u) {
		accountDao.save(u);
	}

	@Transactional(rollbackFor = Exception.class)
	public void update(Account u) {
		accountDao.merge(u);
	}

	public void delete(Account u) {
		accountDao.delete(u);
	}

	@Override
	@Transactional
	public void deleteUser(String email, boolean isActive) {
		Account acc = findItemByProperty("email",email);
		if (isActive == false) {
			acc.setIsActive(true);
		} else {
			acc.setIsActive(false);
		}
	}

	@Override
	public boolean checkExists(String email) {
		return accountDao.checkExist(email);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public boolean savePwd(String email, String pwd) {
		try{
			Account iu = findItemByProperty("email",email);
			iu.setPwd(pwd);
			return true;
		}catch (Exception e) {
			logger.error("Fail to update user for " + email + ":", e);
			return false;
		}
	}

	@Override
	public List<Account> getAllUsers(PagingInfo pInfo) {
		return accountDao.findAll(pInfo);
	}
	
	@Override
	public List<Account> getUsers(String kw, PagingInfo pInfo) {
		return accountDao.getUsers(kw,pInfo);
	}

	@Override
	public Account findItemByProperty(String column, Object value) {
		List<Account> lst = findByProperty(column, value);
		return (lst.size() > 0)?lst.get(0):null;
	}

	@Override
	public void updateColumLong(long id, String column, int i) {
		try{
			Account acc = findById(id);
			if(acc != null){
				if(column.equals("toCoin")){
					acc.setToCoin(i);
				}else if(column.equals("liked")){
					acc.setLiked(i);
				}
				accountDao.merge(acc);
			}
		}catch (Exception e) {
			// TODO: handle exception
		}
	}

	@Override
	public Account findById(Long idAccount) {
		return accountDao.findById(idAccount);
	}

	@Override
	public void updateColumnString(long id, String column, String value) {
		try{
			Account acc = findById(id);
			if(acc != null){
				if(column.equals("pwd")){
					acc.setPwd(value);
				}else if(column.equals("avatar")){
					acc.setAvatar(value);
				}
				accountDao.merge(acc);
			}
		}catch (Exception e) {
			// TODO: handle exception
		}
	}

	@Override
	public List<Account> getAllLst(String kwSearch, String sort, String sortBy,
			PagingInfo pInfo) {
		return accountDao.getAllLst(kwSearch,sort,sortBy,pInfo);
	}

	@Override
	public void updateBoolean(Long id, String column) {
		try{
			Account acc = findById(id);
			if(acc != null){
				if(column.equals("isActive")){
					if(acc.getIsActive()){
						acc.setIsActive(false);
					}else{
						acc.setIsActive(true);
					}
				}
				accountDao.merge(acc);
			}
		}catch (Exception e) {
			// TODO: handle exception
		}
	}

	@Override
	public boolean updateAccount(Long id, int level, String fullName,
			String avatar, String address, String mobile, boolean sex,
			Date birthDay, long liked, long toCoin) {
		boolean flag = false;
		try{
			Account acc = findById(id);
			if(acc != null){
				acc.setLevel(level);
				acc.setFullName(fullName);
				acc.setAvatar(avatar);
				acc.setAddress(address);
				acc.setMobile(mobile);
				acc.setSex(sex);
				acc.setBirthDay(birthDay);
				acc.setLiked(liked);
				acc.setToCoin(toCoin);
				accountDao.merge(acc);
				flag=true;
			}
		}catch (Exception e) {
			return flag;
		}
		return flag;
	}

	@Override
	public boolean updateAccountInfo(Long id, String fullName, String address,
			String mobile, boolean sex, Date birthD) {
		boolean flag = false;
		try{
			Account acc = findById(id);
			if(acc != null){
				acc.setFullName(fullName);
				acc.setAddress(address);
				acc.setMobile(mobile);
				acc.setSex(sex);
				accountDao.merge(acc);
				flag=true;
			}
		}catch (Exception e) {
			return flag;
		}
		return flag;
	}

	@Override
	public boolean updatePwdAccount(Long id, String pwd) {
		boolean flag = false;
		try{
			Account acc = findById(id);
			acc.setPwd(pwd);
			accountDao.merge(acc);
			flag=true;
		}catch (Exception e) {
			return flag;
		}
		return flag;
	}

}
